home *** CD-ROM | disk | FTP | other *** search
- /* New "TEX" master XFCN "zbrowser(...)" - by ^z - 1988 Aug 22-Sep 4...
- *
- * copyright 1988 by Mark "^z" Zimmermann - all rights reserved.
- * (Symantec/Think Technologies may also have copyrights on portions.)
- * All standard legalistic disclaimers are included herewith by reference.
- * If, for example, your use of this software causes a thermonuclear
- * war and the destruction of civilization (as we know it), I am
- * not responsible. Use the program at your own risk!!! I have
- * never lost any data with it -- but there are no guarantees in life.
- *
- * This XFCN was developed using Think's Lightspeed C. It is available
- * for nonexclusive licensing at a cost of 2% of retail price,
- * per distributed copy of any for-sale software
- * that uses it (as of Sept. 1988). My HyperCard stack using this XFCN,
- * "TEX", is available via CompuServe, Arpanet, and other media.
- *
- ***********************************************************************
- * Individual users of TEX should send a $10 license fee to me at *
- * the below address. The corporate license fee is $40 per copy in *
- * simultaneous use. *
- ***********************************************************************
- *
- * In exchange for your license fee, you get:
- * - information about extensions and enhancements to this program
- * as they are come out;
- * - free support and advice on its use;
- * - copies of new versions for the cost of media and reproduction;
- * - a nice warm feeling knowing that you are supporting further
- * research into massive free-text dataspace tool-building.
- *
- * Be a part of the adventure! If you can't afford cash, take the time
- * to write me a nice letter about how you're using TEX, or send a
- * disk with your ideas/suggestions/modifications to the stack.
- *
- * All monies received are used to enhance this software and to pay for
- * creation and distribution expenses. Comparable "commercial" free-text
- * database products cost hundreds or thousands of dollars. You have
- * something precious here -- take advantage of it, please! Build upon
- * my work, extend it, and share your results with the community of
- * researchers.
- *
- * I am very proud of this software. I have chosen to distribute it
- * at nominal cost, so that it can be more widely used and can help
- * more people. Please join me!
- *
- * For further information, and for license fee payments, write:
- * Mark ^Zimmermann
- * 9511 Gwyndale Drive
- * Silver Spring, MD 20910
- * USA
- *
- * Be sure to enclose a STAMPED, SELF-ADDRESSED ENVELOPE if you want
- * to receive a timely reply!
- *
- * Electronic addresses:
- * science@nems.arpa
- * [75066,2044] CompuServe
- * tel. 301-565-2166
- */
-
-
- /* see the end of this file for detailed information on the various
- * TEX functions and their calling conventions
- */
-
- /* ------------------------header files to include-------------- */
-
- #include <MacTypes.h>
- #include <FileMgr.h>
- #include <HyperXCmd.h>
-
-
- /* ---------------declarations and definitions------------------- */
-
-
- /* KEY_LENGTH is the number of letters we have in each index record;
- * 28 is the value chosen for the past year as optimal...don't
- * change it without good reason!
- */
- #define KEY_LENGTH 28
-
- /* SUBSPACE_QUANTUM is the distance in bytes that defines the proximity
- * neighborhood of a word in the index ... it is used when defining a
- * subset for pseudo-proximity searching.... SUBSPACE_QUANTUM * 8 is the
- * effective compression factor for squeezing the text file down into
- * an array of one-bit flags showing which regions of the dataspace are
- * in the current working subspace.
- *
- * Thus, SUBSPACE_QUANTUM = 32, a nice choice, defines a chunkiness of 32
- * characters in making comparisons for proximity determination purposes,
- * and results in a compression factor of 256. Thus, in a typical 1 MB Mac
- * with ~100kB free running HyperCard, that should allow subspace browsing
- * of dataspaces up to ~25 MB....
- */
- #define SUBSPACE_QUANTUM 32
-
- /* structure of the records in the index key file:
- * a fixed-length character string, padded out with blanks and
- * containing the unique alphanumeric 'words' in the document
- * file, changed to all-capital letters;
- * a cumulative count of how many total occurrences of words, including
- * the current one, have appeared up to this point in the sorted
- * index.
- */
- typedef struct
- {
- char kkey[KEY_LENGTH];
- long ccount;
- } KEY_RECORD;
-
- /* some symbolic values... */
- #define TRUE 1
- #define FALSE 0
- #define NULL 0
-
- /* define these to allow use of global variables in my XFCNs; a
- * Lightspeed C necessity, apparently...
- */
-
- #define SetUpA4() asm { move.l a4,-(sp) \
- move.l a0,a4 }
-
- #define RestoreA4() asm { move.l (sp)+,a4 }
-
-
- /* ---------------prototypes------------------- */
-
- pascal void main (XCmdBlockPtr paramPtr);
- void doContext (XCmdBlockPtr paramPtr);
- void doEmptySubspace (XCmdBlockPtr paramPtr);
- void doFillSubspace (XCmdBlockPtr paramPtr);
- void doIndex (XCmdBlockPtr paramPtr);
- void doLocate (XCmdBlockPtr paramPtr);
- void doNewSubspace (XCmdBlockPtr paramPtr);
- void doReleaseSubspace (XCmdBlockPtr paramPtr);
- void doSetSubspaceBits (XCmdBlockPtr paramPtr);
- void doText (XCmdBlockPtr paramPtr);
- void returnErrorMsg (XCmdBlockPtr paramPtr, char *msg);
- void getKeyRecord (KEY_RECORD *keyRecp, long keyRecNum, int keyFileRefNum);
- long getTextPtr (long instanceNum, int ptrFileRefNum);
- void getContextLine (long bytes, long textPtr, int refNum, char *ansp);
- void buildIndexAnswer (char *ansp, int indexCountWidth, int indexKeyWidth,
- long count, char key[]);
- void buildSubIndexAnswer (char *ansp, int indexCountWidth,
- int indexKeyWidth, long maxIndexSampleCount, long prevCcount,
- long thisCcount, char key[], Handle subspaceHandle, int ptrFileRefNum);
- int inSubspace (long textPtr, Handle subspaceHandle);
- void setSSBit (long textPtr, int setOrClear, Handle subspaceHandle);
- char *strcpy (char *s1, char *s2);
- int strlen (char *s);
- int zstrcmp (unsigned char *s1, unsigned char *s2);
- long atol (char *s);
- void ltoaR (char *ansp, long n, int maxDigits);
-
-
- /* ------------------------main program-------------- */
-
-
- /* main routine, to dispatch control to a function
- * defined by the first letter of the first argument of the XFCN
- */
-
- pascal void main (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- SetUpA4();
- switch (**(paramPtr->params[0]))
- {
- case 'C':
- doContext (paramPtr);
- break;
- case 'E':
- doEmptySubspace (paramPtr);
- break;
- case 'F':
- doFillSubspace (paramPtr);
- break;
- case 'I':
- doIndex (paramPtr);
- break;
- case 'L':
- doLocate (paramPtr);
- break;
- case 'N':
- doNewSubspace (paramPtr);
- break;
- case 'R':
- doReleaseSubspace (paramPtr);
- break;
- case 'S':
- doSetSubspaceBits (paramPtr);
- break;
- case 'T':
- doText (paramPtr);
- break;
- default:
- returnErrorMsg (paramPtr,
- "{Sorry, unrecognized command in TEX zbrowser XFCN call!}");
- break;
- }
- RestoreA4();
- return;
- }
-
-
- /* ---------------------major functional units-------------- */
-
-
- /* function to create the context display...
- *
- * ("CONTEXT", instanceNum, contextLines, targetContextLine,
- * contextLineLength, contextWordOffset, maxContextLinesSkipped,
- * ptrFileRefNum, textFileRefNum, subspaceHandle)
- * -- returns with contextLines of display followed by contextLines
- * of instanceNum-textPtr pairs, with context instance instanceNum
- * on line targetContextLine...
- */
-
- void doContext (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- int contextLines, targetContextLine, contextLineLength,
- contextWordOffset, ptrFileRefNum, textFileRefNum, line;
- long instanceNum, maxContextLinesSkipped, textPtr, j, *tempNum;
- Handle subspaceHandle, answer, tempStor;
- char *ansp;
-
- if (paramPtr->paramCount != 10)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, wrong number of parameters in XFCN CONTEXT call!}");
- return;
- }
-
- instanceNum = atol (*(paramPtr->params[1]));
- contextLines = atol (*(paramPtr->params[2]));
- targetContextLine = atol (*(paramPtr->params[3]));
- contextLineLength = atol (*(paramPtr->params[4]));
- contextWordOffset = atol (*(paramPtr->params[5]));
- maxContextLinesSkipped = atol (*(paramPtr->params[6]));
- ptrFileRefNum = atol (*(paramPtr->params[7]));
- textFileRefNum = atol (*(paramPtr->params[8]));
- subspaceHandle = (Handle) atol (*(paramPtr->params[9]));
-
- if (instanceNum < 0 || contextLines < 1 || targetContextLine < 1 ||
- targetContextLine > contextLines ||
- contextLineLength < KEY_LENGTH + contextWordOffset ||
- contextWordOffset < 0 || maxContextLinesSkipped < 1 ||
- ptrFileRefNum == NULL || textFileRefNum == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, bad parameter in XFCN CONTEXT call!}");
- return;
- }
-
- if ((answer = NewHandle (contextLines * (contextLineLength + 26) + 1))
- == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, out of memory error in XFCN CONTEXT call!}");
- return;
- }
-
- /* tempStor is used to store values for instanceNums and textPtrs */
- if ((tempStor = NewHandle (contextLines * 2 * sizeof(long))) == NULL)
- {
- DisposHandle (answer);
- returnErrorMsg (paramPtr,
- "{Sorry, secondary out-of-memory error in XFCN CONTEXT call!}");
- return;
- }
-
- /* back up to the right starting instanceNum */
- for (line = targetContextLine; line > 1; --line)
- {
- for (j = 0; j < maxContextLinesSkipped; ++j)
- {
- textPtr = getTextPtr (--instanceNum, ptrFileRefNum);
- if (subspaceHandle == NULL || textPtr < 0 ||
- inSubspace (textPtr, subspaceHandle))
- break;
- }
- }
-
- HLock (answer);
- HLock (tempStor);
- ansp = *answer;
- tempNum = (long *) *tempStor;
-
- /* generate the lines of the context display, saving numbers */
- for (line = 0; line < contextLines; ++line)
- {
- for (j = 0; j < maxContextLinesSkipped; ++j, ++instanceNum)
- {
- textPtr = getTextPtr (instanceNum, ptrFileRefNum);
- if (textPtr < 0)
- break;
- if (subspaceHandle == NULL ||
- inSubspace (textPtr, subspaceHandle))
- {
- getContextLine (contextLineLength,
- textPtr - contextWordOffset, textFileRefNum, ansp);
- ansp += contextLineLength;
- break;
- }
- }
- if (j == maxContextLinesSkipped)
- {
- textPtr = -1;
- --instanceNum;
- for (j = 0; j < contextLineLength; ++j)
- *ansp++ = '.';
- }
- tempNum[line] = instanceNum++;
- tempNum[line + contextLines] = textPtr;
- *ansp++ = '\r';
- }
-
- for (line = 0; line < contextLines; ++line)
- {
- ltoaR (ansp, tempNum[line], 12);
- ansp += 12;
- ltoaR (ansp, tempNum[line + contextLines], 12);
- ansp += 12;
- *ansp++ = '\r';
- }
-
- *ansp = '\0';
- HUnlock (answer);
- paramPtr->returnValue = answer;
- HUnlock (tempStor);
- DisposHandle (tempStor);
- return;
- }
-
-
- /* function to empty out a subspace so that no words are in the valid
- * region:
- *
- * ("EMPTYSUBSPACE", subspaceHandle)
- * -- returns quietly with nothing if it successfully sets all bits
- * in the subspace flag array to zero; beeps and gives an error msg
- * if it fails somehow...
- */
-
- void doEmptySubspace (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- long subspaceSize;
- Handle subspaceHandle;
- register char *cp, *endOfSubspace;
-
- if (paramPtr->paramCount != 2)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, wrong # of parameters in XFCN EMPTYSUBSPACE call!}");
- return;
- }
-
- subspaceHandle = (Handle) atol (*(paramPtr->params[1]));
-
- if (subspaceHandle == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, NULL subspaceHandle in XFCN EMPTYSUBSPACE call!}");
- return;
- }
-
- subspaceSize = GetHandleSize (subspaceHandle);
- endOfSubspace = *subspaceHandle + subspaceSize;
- for (cp = *subspaceHandle; cp < endOfSubspace; ++cp)
- *cp = 0x00;
-
- return;
- }
-
-
- /* function to fill a subspace so that the entire dataspace is in the
- * valid region:
- *
- * ("FILLSUBSPACE", subspaceHandle)
- * -- returns quietly with nothing if it successfully sets all bits
- * in the subspace flag array to one; beeps and gives an error msg
- * if failure...
- */
-
- void doFillSubspace (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- long subspaceSize;
- Handle subspaceHandle;
- register char *cp, *endOfSubspace;
-
- if (paramPtr->paramCount != 2)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, wrong # of parameters in XFCN FILLSUBSPACE call!}");
- return;
- }
-
- subspaceHandle = (Handle) atol (*(paramPtr->params[1]));
-
- if (subspaceHandle == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, NULL subspaceHandle in XFCN FILLSUBSPACE call!}");
- return;
- }
-
- subspaceSize = GetHandleSize (subspaceHandle);
- endOfSubspace = *subspaceHandle + subspaceSize;
- for (cp = *subspaceHandle; cp < endOfSubspace; ++cp)
- *cp = 0xFF;
-
- return;
- }
-
-
- /* function to produce the index window display and associated info
- *
- * ("INDEX", wordNum, indexLines, maxIndexSampleCount, indexCountWidth,
- * indexKeyWidth, keyFileRefNum, ptrFileRefNum, subspaceHandle)
- * -- returns with indexLines of index window display, followed by
- * indexLines of instanceNums. The index lines are:
- * indexCountWidth columns of occurrence count info (right-justified),
- * a blank column, and indexKeyWidth columns of keyWord (in all
- * caps, left-justified). Demand that indexCountWidth be at least
- * 5, to allow for subindex count display, and that indexKeyWidth
- * be in the range 1 through KEY_LENGTH = 28 ...
- */
-
- void doIndex (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- KEY_RECORD thisRec, prevRec;
- register int i;
- int indexLines, keyFileRefNum, indexCountWidth, ptrFileRefNum,
- keyRecsFound, indexKeyWidth;
- long wordNum, maxIndexSampleCount;
- Handle subspaceHandle, answer;
- char *ansp;
-
- if (paramPtr->paramCount != 9)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, wrong number of parameters in XFCN INDEX call!}");
- return;
- }
-
- wordNum = atol (*(paramPtr->params[1]));
- indexLines = atol (*(paramPtr->params[2]));
- maxIndexSampleCount = atol (*(paramPtr->params[3]));
- indexCountWidth = atol (*(paramPtr->params[4]));
- indexKeyWidth = atol (*(paramPtr->params[5]));
- keyFileRefNum = atol (*(paramPtr->params[6]));
- ptrFileRefNum = atol (*(paramPtr->params[7]));
- subspaceHandle = (Handle) atol (*(paramPtr->params[8]));
-
- if (wordNum < 0 || indexLines < 1 || maxIndexSampleCount < 1 ||
- indexCountWidth < 5 || indexKeyWidth < 1 ||
- indexKeyWidth > KEY_LENGTH || keyFileRefNum == 0 ||
- ptrFileRefNum == 0)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, bad parameter in XFCN INDEX call!}");
- return;
- }
-
-
- if ((answer = NewHandle (indexLines *
- (indexCountWidth + indexKeyWidth + 15) + 1)) == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, out of memory error in XFCN INDEX call!}");
- return;
- }
-
- HLock (answer);
- ansp = *answer;
-
- getKeyRecord (&prevRec, wordNum - 1, keyFileRefNum);
-
- for (i = 0; i < indexLines; ++i)
- {
- getKeyRecord (&thisRec, wordNum + i, keyFileRefNum);
- if (thisRec.ccount == 0)
- break;
-
- if (subspaceHandle == NULL)
- buildIndexAnswer (ansp, indexCountWidth, indexKeyWidth,
- thisRec.ccount - prevRec.ccount, thisRec.kkey);
- else
- buildSubIndexAnswer (ansp, indexCountWidth, indexKeyWidth,
- maxIndexSampleCount, prevRec.ccount, thisRec.ccount,
- thisRec.kkey, subspaceHandle, ptrFileRefNum);
-
- ansp += indexCountWidth + indexKeyWidth + 2;
- prevRec.ccount = thisRec.ccount;
- }
-
- keyRecsFound = i;
- for (i = keyRecsFound; i < indexLines; ++i)
- *ansp++ = '\r';
-
- for (i = 0; i < keyRecsFound; ++i)
- {
- getKeyRecord (&thisRec, wordNum + i - 1, keyFileRefNum);
- ltoaR (ansp, thisRec.ccount, 12);
- ansp += 12;
- *ansp++ = '\r';
- }
-
- for (i = keyRecsFound; i < indexLines; ++i)
- *ansp++ = '\r';
-
- *ansp = '\0';
- HUnlock (answer);
- paramPtr->returnValue = answer;
- return;
- }
-
-
- /* function to find a chosen string in the index key file (just do a
- * binary search to locate it):
- *
- * ("LOCATE", targetString, keyFileRefNum)
- * -- returns wordNum for the targetString if it is found in the
- * key file; otherwise returns wordNum for the word alphabetically
- * preceding targetString followed by "{targetString not found!}"
- * on the second line of the answer...
- *
- */
-
- void doLocate (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- register int i, c;
- int keyFileRefNum, diff;
- char *cp;
- register long mid;
- long low, high, keyFileSize;
- KEY_RECORD thisRec, targetRec;
- Handle answer;
-
- if (paramPtr->paramCount != 3)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, wrong number of parameters in XFCN LOCATE call!}");
- return;
- }
-
- keyFileRefNum = atol (*(paramPtr->params[2]));
-
- if (keyFileRefNum == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, NULL keyFileRefNum error in XFCN LOCATE call!}");
- return;
- }
-
- cp = *(paramPtr->params[1]);
- for (i = 0; i < KEY_LENGTH; ++i)
- {
- c = *cp;
- if (c == '\0')
- {
- targetRec.kkey[i] = ' ';
- continue;
- }
- if (c >= 'a' && c <= 'z')
- c = c - 'a' + 'A';
- targetRec.kkey[i] = c;
- ++cp;
- }
-
- low = 0;
- GetEOF (keyFileRefNum, &keyFileSize);
- high = keyFileSize / sizeof (KEY_RECORD) - 1;
-
- while (low <= high)
- {
- mid = (low + high) / 2;
- getKeyRecord (&thisRec, mid, keyFileRefNum);
- if (thisRec.ccount == 0)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, possible file I/O error in XFCN LOCATE call!}");
- return;
- }
- diff = zstrcmp ((unsigned char *)targetRec.kkey,
- (unsigned char *)thisRec.kkey);
- if (diff < 0)
- high = mid - 1;
- else if (diff > 0)
- low = mid + 1;
- else
- break;
- }
-
- if (diff < 0)
- --mid;
- if (mid < 0)
- mid = 0;
-
- if ((answer = NewHandle (64)) == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, out of memory error in XFCN LOCATE call!}");
- return;
- }
- ltoaR (*answer, mid, 12);
- *(*answer + 12) = '\0';
- if (diff != 0)
- strcpy (*answer + 12, "\r{target string not found!}");
- paramPtr->returnValue = answer;
- return;
- }
-
-
- /* function to create a new subspace:
- *
- * ("NEWSUBSPACE", textFileRefNum)
- * -- returns subspaceHandle for a new subspace that it creates, big
- * enough to do subspace browsing -- but does NOT initialize that
- * subspace or check to see whether another subspace already
- * exists. Beeps and gives error msg if it fails...
- */
-
- void doNewSubspace (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- int textFileRefNum;
- long textFileSize, subspaceSize;
- Handle subspaceHandle, answer;
-
- if (paramPtr->paramCount != 2)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, wrong # of parameters in XFCN NEWSUBSPACE call!}");
- return;
- }
-
- textFileRefNum = atol (*(paramPtr->params[1]));
- if (textFileRefNum == NULL ||
- GetEOF (textFileRefNum, &textFileSize) != noErr)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, file error in XFCN NEWSUBSPACE call!}");
- return;
- }
-
- subspaceSize = 1 + textFileSize / (SUBSPACE_QUANTUM * 8);
-
- if ((subspaceHandle = NewHandle (subspaceSize)) == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, not enough memory for subspace creation!}");
- return;
- }
-
- if ((answer = NewHandle (16)) == NULL)
- {
- DisposHandle (subspaceHandle);
- returnErrorMsg (paramPtr,
- "{Sorry, out of memory error in XFCN NEWSUBSPACE call!}");
- return;
- }
- ltoaR (*answer, (long)subspaceHandle, 12);
- *(*answer + 12) = '\0';
- paramPtr->returnValue = answer;
-
- return;
-
- }
-
-
- /* routine to get rid of a subspace and release that memory:
- *
- * ("RELEASESUBSPACE", subspaceHandle)
- * -- returns quietly with nothing if successful in releasing the
- * subspaceHandle, or noisily with an error message if it fails...
- */
-
- void doReleaseSubspace (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- Handle subspaceHandle;
-
- if (paramPtr->paramCount != 2)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, wrong # of params in XFCN RELEASESUBSPACE call!}");
- return;
- }
-
- subspaceHandle = (Handle) atol (*(paramPtr->params[1]));
- if (subspaceHandle == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, NULL subspaceHandle in XFCN RELEASESUBSPACE call!}");
- return;
- }
-
- DisposHandle (subspaceHandle);
- return;
- }
-
-
- /* function to turn on or off bits in a subspace according to their
- * proximity to a given word's occurrences:
- *
- * ("SETSUBSPACEBITS", wordNum, neighborhoodSize, setOrClear,
- * keyFileRefNum, ptrFileRefNum, subspaceHandle)
- * -- returns quietly with nothing if it is successful in setting or
- * clearing (depending on setOrClear's value, 0 or non-0) the
- * bits in the subspace flag array in the neighborhood of the
- * chosen word(s); gives an error msg if there was a problem.
- * neighborhoodSize is in characters and is used to determine
- * how many bits to set/clear on each side of the instances...
- */
-
- void doSetSubspaceBits (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- long wordNum, neighborhoodSize, bitsToSet, maxTextPtr, instance,
- tp0, tp, tpMax;
- int setOrClear, keyFileRefNum, ptrFileRefNum;
- Handle subspaceHandle;
- KEY_RECORD prevRec, thisRec;
-
- if (paramPtr->paramCount != 7)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, wrong # of parameters in XFCN SETSUBSPACE call!}");
- return;
- }
-
-
- wordNum = atol (*(paramPtr->params[1]));
- neighborhoodSize = atol (*(paramPtr->params[2]));
- setOrClear = atol (*(paramPtr->params[3]));
- keyFileRefNum = atol (*(paramPtr->params[4]));
- ptrFileRefNum = atol (*(paramPtr->params[5]));
- subspaceHandle = (Handle) atol (*(paramPtr->params[6]));
-
- if (wordNum < 0 || neighborhoodSize < 1 ||
- keyFileRefNum == NULL || ptrFileRefNum == NULL ||
- subspaceHandle == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, bad parameter in XFCN SETSUBSPACE call!}");
- return;
- }
-
- bitsToSet = (neighborhoodSize * 2) / SUBSPACE_QUANTUM + 1;
- maxTextPtr = GetHandleSize (subspaceHandle) * SUBSPACE_QUANTUM * 8;
- getKeyRecord (&prevRec, wordNum - 1, keyFileRefNum);
- getKeyRecord (&thisRec, wordNum, keyFileRefNum);
-
- for (instance = prevRec.ccount; instance < thisRec.ccount; ++instance)
- {
- tp0 = getTextPtr (instance, ptrFileRefNum);
- if (tp0 < 0)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, getTextPtr I/O error in XFCN SETSUBSPACE call!}");
- return;
- }
- tp = tp0 - (bitsToSet / 2) * SUBSPACE_QUANTUM;
- tpMax = tp + bitsToSet * SUBSPACE_QUANTUM;
- if (tp < 0)
- tp = 0;
- if (tpMax > maxTextPtr)
- tpMax = maxTextPtr;
- for ( ; tp < tpMax; tp += SUBSPACE_QUANTUM)
- setSSBit (tp, setOrClear, subspaceHandle);
- }
-
- return;
- }
-
-
- /* function to grab a chunk of text:
- *
- * ("TEXT", textPtr, textChunkSize, textOffset, textFileRefNum)
- * -- returns with (if possible; see below)
- * textChunkSize bytes of text from the text file,
- * starting at byte number textPtr-textOffset+1 and ending
- * just before byte number textPtr-textOffset+textChunkSize+1.
- * (The '+1' is to match up with HyperCard's 1-based counting
- * convention, rather than the 0-based C convention!!)
- * If the file isn't big enough or if textPtr is too near the
- * beginning or end of the file, cut off the retrieved text
- * at that boundary and insert the words {beginning of dataspace}
- * or {end of dataspace}. ***Do no filtering of the text!***
- * (Thus, there may be strangenesses if the 'text' file has
- * '\0' or other nasty characters in it -- sorry about that!)
- * Restrict textChunkSize to <32000 bytes. After the text, on
- * a separate line, return three numbers: the byte number of
- * the first char returned relative to the beginning of the text
- * file, the actual offset within the characters returned
- * of the originally-requested textPtr, and the byte number
- * of the character after the last char returned relative to
- * the beginning of the text file.
- */
-
- void doText (paramPtr)
- XCmdBlockPtr paramPtr;
- {
- int textFileRefNum;
- long textPtr, textChunkSize, textOffset, textFileSize, startText,
- endText, count;
- Handle answer;
- char *ansp;
-
- if (paramPtr->paramCount != 5)
- {
- returnErrorMsg (paramPtr,
- "Sorry, wrong number of parameters in XFCN TEXT call!}");
- return;
- }
-
- textPtr = atol (*(paramPtr->params[1]));
- textChunkSize = atol (*(paramPtr->params[2]));
- textOffset = atol (*(paramPtr->params[3]));
- textFileRefNum = atol (*(paramPtr->params[4]));
-
- GetEOF (textFileRefNum, &textFileSize);
-
- if (textPtr < 0 || textPtr > textFileSize || textOffset < 1 ||
- textOffset > textChunkSize || textChunkSize < 1 ||
- textChunkSize > 32000 || textFileRefNum == 0)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, bad parameter in XFCN TEXT call!}");
- return;
- }
-
- startText = textPtr - textOffset + 1;
- if (startText < 0)
- startText = 0;
- endText = textPtr + textChunkSize - textOffset + 1;
- if (endText > textFileSize)
- endText = textFileSize;
-
- count = endText - startText;
- if ((answer = NewHandle (count + 80)) == NULL)
- {
- returnErrorMsg (paramPtr,
- "{Sorry, out of memory error in XFCN TEXT call!}");
- return;
- }
-
- HLock (answer);
- ansp = *answer;
- if (startText == 0)
- {
- strcpy (ansp, "{beginning of dataspace}\r");
- ansp += strlen ("{beginning of dataspace}\r");
- textOffset = textPtr + strlen ("{beginning of dataspace}\r");
- }
-
- if (SetFPos (textFileRefNum, fsFromStart, startText) != noErr ||
- FSRead (textFileRefNum, &count, ansp) != noErr)
- {
- SysBeep (10);
- strcpy (ansp,
- "{Sorry, file I/O error in XFCN TEXT call!}");
- HUnlock (answer);
- paramPtr->returnValue = answer;
- return;
- }
-
- ansp += count;
- *ansp++ = '\r';
- if (endText == textFileSize)
- {
- strcpy (ansp, "{end of dataspace}\r");
- ansp += strlen ("{end of dataspace}\r");
- }
- ltoaR (ansp, startText, 12);
- ansp += 12;
- ltoaR (ansp, textOffset, 12);
- ansp += 12;
- ltoaR (ansp, endText, 12);
- ansp += 12;
- *ansp = '\0';
- HUnlock (answer);
- paramPtr->returnValue = answer;
- return;
- }
-
-
-
- /* ------------------------support routines-------------- */
-
-
- /* function to set the return value of the XFCN to a chosen error msg;
- * if there isn't enough free memory to give us a Handle to the msg,
- * beep a bunch and then return!
- */
-
- void returnErrorMsg (paramPtr, msg)
- XCmdBlockPtr paramPtr;
- char *msg;
- {
- Handle answer;
- int msgLength;
-
- SysBeep (10);
- msgLength = strlen (msg);
- if ((answer = NewHandle (1 + msgLength)) == NULL)
- {
- SysBeep (10);
- SysBeep (10);
- SysBeep (10);
- SysBeep (10);
- SysBeep (10);
- return;
- }
-
- strcpy (*answer, msg);
- paramPtr->returnValue = answer;
- return;
- }
-
-
- /* function to fetch an index key record from the key file; if an
- * illegal keyRecNum is asked for, or if any sort of I/O error is
- * reported by SetFPos() or FSRead(), return 0 ccount and blank kkey....
- */
-
- void getKeyRecord (keyRecp, keyRecNum, keyFileRefNum)
- KEY_RECORD *keyRecp;
- long keyRecNum;
- int keyFileRefNum;
- {
- long count;
- register int i;
-
- count = sizeof(KEY_RECORD);
-
- if (keyRecNum < 0 ||
- SetFPos (keyFileRefNum, fsFromStart,
- keyRecNum * sizeof(KEY_RECORD)) != noErr ||
- FSRead (keyFileRefNum, &count, keyRecp) != noErr)
- {
- for (i = 0; i < KEY_LENGTH; ++i)
- keyRecp->kkey[i] = ' ';
- keyRecp->ccount = 0;
- }
-
- return;
- }
-
- /* function to fetch the value of the nth ptr from file ptrFileRefNum;
- * return illegal value (-1) for result if something goes wrong....
- */
-
- long getTextPtr (n, ptrFileRefNum)
- long n;
- int ptrFileRefNum;
- {
- long bytes = sizeof(long), result;
-
- if (SetFPos (ptrFileRefNum, fsFromStart, n * sizeof(long)) != noErr ||
- FSRead (ptrFileRefNum, &bytes, &result) != noErr)
- return (-1);
-
- return (result);
- }
-
-
- /* function to fetch a filtered line of text from the file ... fill
- * in with blanks if try to fetch from before the beginning of the
- * file or after the end of the file ... filter all control characters
- * by turning them into spaces ....
- */
-
- void getContextLine (bytes, start, refNum, ans)
- int refNum;
- long bytes, start;
- char *ans;
- {
- register int i = 0;
- long origbytes;
-
- origbytes = bytes;
- if (start < 0)
- for (i = 0; i < -start; ++i)
- ans[i] = ' ';
- bytes -= i;
-
- if (SetFPos (refNum, fsFromStart, start + i) != noErr ||
- FSRead (refNum, &bytes, ans + i) != noErr)
- {
- if (origbytes >
- strlen ("{Sorry, file I/O error in XFCN CONTEXT call!}"))
- {
- strcpy (ans, "{Sorry, file I/O error in XFCN CONTEXT call!}");
- bytes = origbytes;
- }
- else
- SysBeep (10);
- }
-
- if (bytes + i < origbytes)
- for (i += bytes; i < origbytes; ++i)
- ans[i] = ' ';
- for (i = 0; i < origbytes; ++i)
- if (ans[i] < 32 || ans[i] == 127)
- ans[i] = ' ';
-
- return;
- }
-
-
- /* function to format an index record, with the count right-justified
- * followed by a space, then the key word itself, followed by a '\r'.
- */
-
- void buildIndexAnswer (ansp, cwidth, kwidth, count, key)
- char *ansp, *key;
- long count;
- int cwidth, kwidth;
- {
- register int i;
-
- ltoaR (ansp, count, cwidth);
- ansp += cwidth;
- *ansp++ = ' ';
- for (i = 0; i < kwidth; ++i)
- *ansp++ = *key++;
- *ansp = '\r';
-
- return;
- }
-
-
- /* function to format an index record when working in a subspace; like
- * buildIndexAnswer function above, but with information about how many
- * instances of each word are in the working subspace. Specifically,
- * give a percentage estimate based on the last maxIndexSampleCount
- * instances for a word that occurs more than maxIndexSampleCount times
- * (e.g., " ~37% "), and for less frequently occurring words give the
- * actual fraction of valid/total instances (e.g., " 17/49 ").
- */
-
- void buildSubIndexAnswer (ansp, cwidth, kwidth, maxSample,
- prevCcount, thisCcount, key, subspaceHandle, ptrFileRefNum)
- char *ansp, *key;
- int cwidth, kwidth, ptrFileRefNum;
- long maxSample, prevCcount, thisCcount;
- Handle subspaceHandle;
- {
- long startCcount, instance, goodInstances;
- register int i;
- int goodPercent, subWidth;
-
- if (thisCcount - prevCcount > maxSample)
- startCcount = thisCcount - maxSample;
- else
- startCcount = prevCcount;
-
- goodInstances = 0;
- for (instance = startCcount; instance < thisCcount; ++instance)
- if (inSubspace (getTextPtr (instance, ptrFileRefNum),
- subspaceHandle))
- ++goodInstances;
-
- if (thisCcount - prevCcount > maxSample)
- {
- goodPercent = (100 * goodInstances) / (thisCcount - startCcount);
- *ansp++ = '~';
- ltoaR (ansp, goodPercent, 3);
- ansp += 3;
- *ansp++ = '%';
- for (i = 5; i < cwidth; ++i)
- *ansp++ = ' ';
- }
- else
- {
- subWidth = (cwidth - 1) / 2;
- ltoaR (ansp, goodInstances, subWidth);
- ansp += subWidth;
- *ansp++ = '/';
- ltoaR (ansp, thisCcount - prevCcount, subWidth);
- ansp += subWidth;
- for (i = 2 * subWidth + 1; i < cwidth; ++i)
- *ansp++ = ' ';
- }
-
- *ansp++ = ' ';
- for (i = 0; i < kwidth; ++i)
- *ansp++ = *key++;
- *ansp = '\r';
-
- return;
- }
-
-
- /* function to determine if a given textPtr is in the subspace of
- * interest ... do it by a simple computation and look-up in the
- * bit array of subspace flags...
- */
-
- int inSubspace (textPtr, subspaceHandle)
- long textPtr;
- Handle subspaceHandle;
- {
- int bitNum;
- long byteNum;
-
- bitNum = (textPtr % (8 * SUBSPACE_QUANTUM)) / SUBSPACE_QUANTUM;
- byteNum = textPtr / (8 * SUBSPACE_QUANTUM);
-
- return (((*subspaceHandle)[byteNum] >> bitNum) & 1);
- }
-
-
- /* function to set or clear a bit in the subspace array...
- */
-
- void setSSBit (textPtr, setOrClear, subspaceHandle)
- long textPtr;
- int setOrClear;
- Handle subspaceHandle;
- {
- int bitNum;
- long byteNum;
-
- bitNum = (textPtr % (8 * SUBSPACE_QUANTUM)) / SUBSPACE_QUANTUM;
- byteNum = textPtr / (8 * SUBSPACE_QUANTUM);
-
- if (setOrClear)
- *(*subspaceHandle + byteNum) |= 1 << bitNum;
- else
- *(*subspaceHandle + byteNum) &= ~(1 << bitNum);
-
- return;
- }
-
-
- /* function to copy a string from one place to another, in a rather
- * obvious fashion ... adapted from the LSC library, and K&R p.101 ....
- */
-
- char *strcpy (s1, s2)
- register char *s1, *s2;
- {
- char *s = s1;
-
- while (*s1++ = *s2++)
- ;
- return (s);
- }
-
-
- /* function to determine the length of a string ... standard thing,
- * adapted from the LSC library, and K&R p.98 ....
- */
-
- int strlen (s)
- register char *s;
- {
- char *s0 = s;
-
- while (*s++)
- ;
- return (s - s0 - 1);
- }
-
- /* my function to compare two strings and give a result as to who is
- * alphabetically earlier. Note that this is almost the same as strncmp()
- * with the fixed value of KEY_LENGTH as the maximum comparison distance,
- * except that I must be sure to handle the non-ASCII funny letters in
- * the Apple character set properly/consistently ... hence the need to
- * declare s1 and s2 to be type unsigned char *...
- */
-
- int zstrcmp (s1, s2)
- register unsigned char *s1, *s2;
- {
- register int n = KEY_LENGTH;
-
- for (; --n && *s1 == *s2; s1++, s2++)
- if (!*s1)
- break;
-
- return (*s1 - *s2);
- }
-
-
- /* function to convert alphanumeric string to a long int, from K&R & LSC
- * library.... simplified to avoid using isspace() & isdigit() ....
- */
-
- long atol (s)
- register char *s;
- {
- int signflag = 0;
- register long r = 0;
-
- while (*s == ' ')
- s++;
-
- if (*s == '-')
- {
- signflag = 1;
- s++;
- }
- else if (*s == '+')
- s++;
-
- while (*s >= '0' && *s <= '9')
- r = r * 10 + (*s++ - '0');
-
- return (signflag ? -r : r);
- }
-
-
- /* function to convert a number into a string of width maxDigits and
- * store it right-justified, blank-filled on left; based on K&R p. 60
- * example of itoa().
- *
- * Error handling: put a '>' or '<' in leading place to warn of an
- * overflow (no room for digits on a positive or negative number,
- * respectively), and put a '^' in leading place to warn if no room
- * for '-' sign on negative number...
- */
-
- void ltoaR (ansp, n, maxDigits)
- register char *ansp;
- register long n;
- int maxDigits;
- {
- register int i;
- long sign;
-
- i = maxDigits - 1;
- if ((sign = n) < 0)
- n = -n;
-
- do
- {
- ansp[i--] = n % 10 + '0';
- }
- while ((n /= 10) > 0 && i >= 0);
-
- if (i < 0 && n > 0) /* ran out of room with digits still to go */
- {
- if (sign > 0)
- ansp[0] = '>'; /* positive overflow signal */
- else
- ansp[0] = '<'; /* negative overflow signal */
- }
- else
- {
- if (sign < 0)
- if (i >= 0)
- ansp[i--] = '-';
- else
- ansp[0] = '^'; /* no room for '-' sign signal */
- for ( ; i >= 0; --i)
- ansp[i] = ' ';
- }
-
- return;
- }
-
-
-
-
- /* -----------------command syntax & calling conventions--------------- */
-
- /*
- * ("CONTEXT", instanceNum, contextLines, targetContextLine,
- * contextLineLength, contextWordOffset, maxContextLinesSkipped,
- * ptrFileRefNum, textFileRefNum, subspaceHandle)
- * -- returns with contextLines of display followed by contextLines
- * of instanceNum-textPtr pairs, with context instance instanceNum
- * on line targetContextLine...
- *
- * ("EMPTYSUBSPACE", subspaceHandle)
- * -- returns quietly with nothing if it successfully sets all bits
- * in the subspace flag array to zero; beeps and gives an error msg
- * if it fails somehow...
- *
- * ("FILLSUBSPACE", subspaceHandle)
- * -- returns quietly with nothing if it successfully sets all bits
- * in the subspace flag array to one; beeps and gives an error msg
- * if failure...
- *
- * ("INDEX", wordNum, indexLines, maxIndexSampleCount, indexCountWidth,
- * indexKeyWidth, keyFileRefNum, ptrFileRefNum, subspaceHandle)
- * -- returns with indexLines of index window display, followed by
- * indexLines of instanceNums. The index lines are:
- * indexCountWidth columns of occurrence count info (right-justified),
- * a blank column, and indexKeyWidth columns of keyWord (in all
- * caps, left-justified). Demand that indexCountWidth be at least
- * 5, to allow for subindex count display, and that indexKeyWidth
- * be in the range 1 through KEY_LENGTH = 28 ...
- *
- * ("LOCATE", targetString, keyFileRefNum)
- * -- returns wordNum for the targetString if it is found in the
- * key file; otherwise returns wordNum for the word alphabetically
- * preceding targetString followed by "{targetString not found!}"
- * on the second line of the answer...
- *
- * ("NEWSUBSPACE", textFileRefNum)
- * -- returns subspaceHandle for a new subspace that it creates, big
- * enough to do subspace browsing -- but does NOT initialize that
- * subspace or check to see whether another subspace already
- * exists. Beeps and gives error msg if it fails...
- *
- * ("RELEASESUBSPACE", subspaceHandle)
- * -- returns quietly with nothing if successful in releasing the
- * subspaceHandle, or noisily with an error message if it fails...
- *
- * ("SETSUBSPACEBITS", wordNum, neighborhoodSize, setOrClear,
- * keyFileRefNum, ptrFileRefNum, subspaceHandle)
- * -- returns quietly with nothing if it is successful in setting or
- * clearing (depending on setOrClear's value, 0 or non-0) the
- * bits in the subspace flag array in the neighborhood of the
- * chosen word(s); gives an error msg if there was a problem.
- * neighborhoodSize is in characters and is used to determine
- * how many bits to set/clear on each side of the instances...
- *
- * ("TEXT", textPtr, textChunkSize, textOffset, textFileRefNum)
- * -- returns with (if possible; see below)
- * textChunkSize bytes of text from the text file,
- * starting at byte number textPtr-textOffset+1 and ending
- * just before byte number textPtr-textOffset+textChunkSize+1.
- * (The '+1' is to match up with HyperCard's 1-based counting
- * convention, rather than the 0-based C convention!!)
- * If the file isn't big enough or if textPtr is too near the
- * beginning or end of the file, cut off the retrieved text
- * at that boundary and insert the words {beginning of dataspace}
- * or {end of dataspace}. ***Do no filtering of the text!***
- * (Thus, there may be strangenesses if the 'text' file has
- * '\0' or other nasty characters in it -- sorry about that!)
- * Restrict textChunkSize to <32000 bytes. After the text, on
- * a separate line, return three numbers: the byte number of
- * the first char returned relative to the beginning of the text
- * file, the actual offset within the characters returned
- * of the originally-requested textPtr, and the byte number
- * of the character after the last char returned relative to
- * the beginning of the text file.
- */
-
-